package com.mlming.simpledb.backend.dm.dataItem;

import com.google.common.primitives.Bytes;
import com.mlming.simpledb.backend.common.SubArray;
import com.mlming.simpledb.backend.dm.DataManagerImpl;
import com.mlming.simpledb.backend.dm.page.Page;
import com.mlming.simpledb.backend.utils.Parser;
import com.mlming.simpledb.backend.utils.Types;

import java.util.Arrays;

public interface DataItem {

    SubArray getRaw(); // 获取DataItem中实际存储的数据[ValidFlag] [DataSize] [Data]
    SubArray data(); // 获取DataItem中实际存储的数据[ValidFlag] [DataSize] [Data]中的[Data]
    boolean isValid(); // 判断DataItem是否合法
    
    void before(); // 在修改DataItem前必须调用该方法, 来保存旧值OldRaw, 以实现之后的回滚
    void unBefore(); // 在撤销修改时,可以调用该方法
    void after(long xid); // 在修改DataItem后必须调用该方法, 来记录日志

    void release(); // DataItem也会实现缓存, 这里就是释放对该DataItem的一个引用

    void lock();
    void unlock();
    void rLock();
    void rUnLock();

    Page page(); // 获得该DataItem存储所在的页
    int getPgno(); // 获得DataItem存储所在的页的pgno
    short getOffset(); // 获得DataItem存储所在的页的offset
    long getUid(); // 获得DataItem的唯一标识: pgno + offset拼接而成
    byte[] getOldRaw(); // 获取旧值

    // 把raw包裹为[ValidFlag] [DataSize] [Data]
    public static byte[] wrapDataItemRaw(byte[] raw) {
        byte[] valid = new byte[1];
        byte[] size = Parser.short2Byte((short)raw.length);
        return Bytes.concat(valid, size, raw);
    }

    // 从页面的offset处解析处dataItem
    public static DataItem parseDataItem(Page pg, short offset, DataManagerImpl dm) {
        // 1) 获取到整个页面的数据
        byte[] raw = pg.getData();
        // 2) 通过offset定位到当前dataItem, 然后读取出[size] => [Data]的长度
        short size = Parser.parseShort(Arrays.copyOfRange(raw, offset+DataItemImpl.OF_SIZE, offset+DataItemImpl.OF_DATA));
        // 3) [data]长度 + 前面[valid][size]的长度, 就是整个dataItem的数据
        short length = (short)(size + DataItemImpl.OF_DATA);
        // 4) 封装成dataItem
        // 我们会发现, dataItem的数据和页面数据是共享内存的, 只是通过start和end两个字段来进行划分
        return new DataItemImpl(new SubArray(raw, offset, offset+length), new byte[length], pg, pg.getPageNumber(),offset, dm);
    }
    // 设置为不合法无效数据
    public static void setDataItemRawInvalid(byte[] raw) {
        raw[DataItemImpl.OF_VALID] = (byte)DataItemImpl.VALID_FLAG_NOT_VALID;
    }

    // 传入pgno和offset, 获得uid
    public static long getUid(int pgno,short offset) {
        // 拼接pgno和offset
        return Parser.parseLong(Bytes.concat(Parser.int2Byte(pgno),Parser.short2Byte(offset)));
    }
}
